//=============================================================================
// TestGameApp GP1 by Bart Uyttenhove
//=============================================================================
#include "StdAfx.h"
#include "TestGameApp.h"
#include "PhysX.h"
#include "ContentManager.h"
#include "InputStateManager.h"
#include "Level.h"
#include "GraphCamera.h"
#include "Sphere.h"
#include "TrianglePosColor.h"
#include "QuadPosColor.h"

#include "ObjMesh.h"
#include "CubePhysX.h"
#include "Character.h"
#include "PickableItem.h"

#define _USE_MATH_DEFINES
#include "math.h"
#include "meshtoobjwriter.h"
#include "PhysicsMesh.h"
#include "PhysicsDebugRenderer.h"

//New engine features
#include "Terrain.h"
#include "SkyBox.h"
#include "DrawableTex2D.h"
#include "FullscreenQuad.h"
#include "Water.h"
#include "SkyModel.h"

TestGameApp::TestGameApp(HINSTANCE hInstance)
	: D3DApp(hInstance)
	,m_pPhysX(0)
	,m_pLevel(0)
	,m_pGraphCamera(0)
	,m_pCharacter(0)
	,m_pSphere(0)
	,m_pTrianglePosColor(0)
	,m_pQuadPosColor(0)
	,m_Angle(0)
	,m_pTerrain(0)
	,m_pSkyBox(0)
	,m_pRefractionPtr(0)
	,m_pRefractionTexturePtr(0)
	,m_pReflectionPtr(0)
	,m_pFullScreenPtr(0)
	,m_pWaterPtr(0)
	,m_pSkymodel(0)
{

}

TestGameApp::~TestGameApp()
{
	SafeDelete(m_pLevel);
	SafeDelete(m_pRefractionPtr);

	SafeDelete(m_pReflectionPtr);
	//SafeDelete(m_pPhysX);
}

void TestGameApp::InitApp()
{
	//choose window title
	m_MainWndCaption = L"D3D10 Test Application";
	D3DApp::InitApp();

	m_pPhysX = new PhysX();
	m_pPhysX->Init();

	D3DXVECTOR3 lightPos;
	lightPos.x = 300.0f;
	lightPos.y = 200.0f;
	lightPos.z = 300.0f;

	//Sunlight setting
	m_DirLight.ambient  = D3DXCOLOR(0.3f, 0.3f, 0.3f, 0.3f);
	m_DirLight.diffuse  = D3DXCOLOR(1.0f, 1.0f, 1.0f, 1.0f);
	m_DirLight.specular = D3DXCOLOR(1.0f, 1.0f, 1.0f, 1.0f);
	m_DirLight.pos = lightPos;
	
	//Distance Fog info
	m_FogInfo.FogStart = 0.0f;
	m_FogInfo.FogEnd = 600.0f;
	m_FogInfo.FogMaxHeight = 250.0f;
	m_FogInfo.FogDensity = 2.0f;
	m_FogInfo.FogMode = RenderContext::FOGMODE_LINEAR;

	D3DXVECTOR3 lightDirection(-0.88935286f, 0.45721933f, 0.0014213272f);

	D3DXVec3Normalize(&m_DirLight.dir, &lightDirection);

	POINT windowsize;
	windowsize.x = m_ClientWidth;
	windowsize.y = m_ClientHeight;

	m_pLevel = new Level(m_pD3DDevice, m_pPhysX, windowsize);

	m_pGraphCamera = new GraphCamera(m_ClientWidth, m_ClientHeight);
	m_pGraphCamera->LookAt(D3DXVECTOR3(4,4,-4),D3DXVECTOR3(0,2,0),D3DXVECTOR3(0,1,0));

	m_pOrthoCamera = new GraphCamera(1024, 512, true);
	m_pOrthoCamera->LookAt(D3DXVECTOR3(4,4,-4),D3DXVECTOR3(0,2,0),D3DXVECTOR3(0,1,0));

	//m_pLevel->AddChild(m_pGraphCamera);
	m_pCharacter = new Character(m_pGraphCamera,D3DXVECTOR3(327.22955,10.141933f,320.12872f),m_pPhysX);
	//m_pCharacter = new Character(m_pGraphCamera,D3DXVECTOR3(72.078499f,24.559767f,92.383362f),m_pPhysX);
	m_pLevel->AddChild(m_pCharacter);


	//Tijdelijk initiaties voor de heightmap te testen
	//Als er meerdere levels komen moet dit herschreven worden
	Terrain::InitInfo tii;
	tii.HeightmapFilename = _T("./Level/Level1/test4.raw");
	tii.TextureLow = _T("./Textures/Environment/sand.dds");
	tii.TextureBase = _T("./Textures/Environment/grass.dds");
	tii.TextureMid = _T("./Textures/Environment/stone.dds");
	tii.TextureHigh = _T("./Textures/Environment/snow.dds");
	tii.NumRows = 500;
	tii.NumCols = 500;
	tii.CellSpacing = 2.0f;
	tii.HeightScale = 0.001f;
	tii.HeightOffset = 0.0f;

	m_pTerrain = new Terrain(m_pLevel, tii);
	m_pLevel->AddChild(m_pTerrain);

	m_pSkyBox = new SkyBox(m_pLevel, _T("./Textures/SkyBox/cubeMap.dds"));
	//m_pLevel->AddChild(m_pSkyBox);

	//Water refraction
	m_pRefractionPtr = new DrawableTex2D(m_pLevel->GetDevice());
	m_pRefractionPtr->Init(m_ClientWidth, m_ClientHeight, true, DXGI_FORMAT_R8G8B8A8_UNORM);

	m_pReflectionPtr = new DrawableTex2D(m_pLevel->GetDevice());
	m_pReflectionPtr->Init(m_ClientWidth, m_ClientHeight, true, DXGI_FORMAT_R8G8B8A8_UNORM);
	
	m_pShadowDepthMapPtr = new DrawableTex2D(m_pLevel->GetDevice());
	m_pShadowDepthMapPtr->Init(1024, 1024, false, DXGI_FORMAT_UNKNOWN);

	m_pFullScreenPtr = new FullscreenQuad(_T("./bitmaps/grassenvmap1024.dds"), m_pLevel);
	m_pFullScreenPtr->Initialize(m_pContentManager);
	
	Water::InitInfo waterinit;
	waterinit.Position = D3DXVECTOR3(-1024.0f, 4.0f, -1024.0f);
	waterinit.Length = 2048;
	waterinit.Width = 2048;
	waterinit.Bumpmaptexture = _T("./Textures/Water/waterbump.dds");
	waterinit.WaveHeight = 0.8f;
	waterinit.Wavelength = 0.01f;
	waterinit.WindForce = 1.0f;
	waterinit.WindDirection = D3DXVECTOR3(-1.0f, 0.0f, 0.0f);

	m_pWaterPtr = new Water(m_pLevel, waterinit);
	m_pLevel->AddChild(m_pWaterPtr);

	SkyModel::InitInfo skymodelinfo;
	skymodelinfo.CenterPosition = D3DXVECTOR3(0, -125, 0);
	skymodelinfo.Type = SkyModel::SkyDome;

	m_pSkymodel = new SkyModel(m_pLevel, skymodelinfo);
	m_pSkymodel->Initialize(m_pContentManager);
	//m_pLevel->AddChild(m_pSkymodel);

	//Initialize all child objects
	m_pLevel->Initialize(m_pContentManager);
}

void TestGameApp::OnResize()
{
	D3DApp::OnResize();
	//OnResize is called before level exists
	if(m_pLevel)m_pLevel->OnResize(m_ClientWidth, m_ClientHeight);
}

void TestGameApp::UpdateScene(const InputState & refInputState)
{
	D3DApp::UpdateScene(refInputState);

	//wait for physx to finish, then get proceed
	m_pPhysX->FetchResults();

	//pass input state to graph
	m_pSkymodel->Tick(refInputState);
	m_pLevel->Tick(refInputState);

	float dTime = refInputState.GetDeltaTime();
	if(dTime>0.3)dTime=0.2f;//prevent dTime from getting too large
	m_Angle+=6.28f/4*dTime;

	D3DXVECTOR3 pos = m_pSkymodel->GetSunDirection() * 500;

	m_pOrthoCamera->LookAt(pos, pos * 0.5f, D3DXVECTOR3(0, 1, 0));

	//laat physics volgende stap berekenen in thread
	m_pPhysX->Simulate(dTime);

}

void TestGameApp::DrawScene()
{
	ResetRenderTargets();

	//Fogkleur toevoegen
	m_FogInfo.FogColor = m_pSkymodel->GetSunColor();

	RenderContext renderContext(m_pGraphCamera, m_DirLight, m_FogInfo);
	RenderContext ShadowmapContext(m_pOrthoCamera, m_DirLight, m_FogInfo);

	Light l;
	//Sunlight setting
	l.ambient  = D3DXCOLOR(0.3f, 0.3f, 0.3f, 0.3f);
	l.diffuse  = m_pSkymodel->GetSunColor();
	l.specular = D3DXCOLOR(1.0f, 1.0f, 1.0f, 1.0f);	

	l.dir = m_pSkymodel->GetSunDirection();
	l.pos = m_pSkymodel->GetSunDirection() * 500;

	
	
	renderContext.SetLight(l);
	ShadowmapContext.SetLight(l);

	RenderShadowDepthMap(&ShadowmapContext);
	//De camera moet terug aangepast worden
	RenderRefraction(&renderContext, m_pWaterPtr->GetHeight());
	RenderReflection(&renderContext, m_pWaterPtr->GetHeight());

	m_pFullScreenPtr->SetTexture(m_pShadowDepthTexturePtr);
	m_pWaterPtr->SetReflectionTexture(m_pReflectionTexturePtr);
	m_pWaterPtr->SetRefractionTexture(m_pRefractionTexturePtr);

	m_pLevel->SetShadowDepthMap(m_pShadowDepthTexturePtr);

	m_pSkymodel->Draw(&renderContext);

	m_pLevel->Draw(&renderContext);
	m_pFullScreenPtr->Draw(&renderContext);
	//draw statistics
	D3DApp::DrawScene();
}
void TestGameApp::ResetRenderTargets()
{
	m_pD3DDevice->OMSetRenderTargets(1, &m_pRenderTargetView, m_pDepthStencilView);
	
	D3D10_VIEWPORT vp;
	vp.TopLeftX = 0;
	vp.TopLeftY = 0;
	vp.Width    = m_ClientWidth;
	vp.Height   = m_ClientHeight;
	vp.MinDepth = 0.0f;
	vp.MaxDepth = 1.0f;

	m_pD3DDevice->RSSetViewports(1, &vp);
}

void TestGameApp::RenderRefraction(const RenderContext* pRenderContext, float waterheight)
{
	D3DXPLANE refractionplane = CreatePlane(D3DXVECTOR3(0.0f, waterheight + 0.5f, 0.0f), D3DXVECTOR3(0.0f, -1.0f, 0.0f));
	
	m_pSkyBox->Visible(false);
	m_pRefractionPtr->Begin();
		m_pSkymodel->DrawRefraction(pRenderContext, refractionplane);
		m_pLevel->DrawRefraction(pRenderContext, refractionplane);
	m_pRefractionPtr->End();

	m_pRefractionTexturePtr = m_pRefractionPtr->GetColorMap();
	m_pSkyBox->Visible(true);
	ResetRenderTargets();
}
void TestGameApp::RenderReflection(const RenderContext* pRenderContext, float waterheight)
{
	D3DXMATRIX reflectionViewMatrix;
	D3DXPLANE reflectionplane = CreatePlane(D3DXVECTOR3(0.0f, waterheight - 1.5f, 0.0f), D3DXVECTOR3(0.0f, 1.0f, 0.0f));
	
	m_pReflectionPtr->Begin();

		m_pGraphCamera->RenderReflection(waterheight);
		reflectionViewMatrix = m_pGraphCamera->GetReflectionViewMatrix();

		m_pSkymodel->DrawReflection(pRenderContext, reflectionViewMatrix, reflectionplane);
		m_pLevel->DrawReflection(pRenderContext, reflectionViewMatrix, reflectionplane);
	m_pReflectionPtr->End();

	m_pReflectionTexturePtr = m_pReflectionPtr->GetColorMap();
	ResetRenderTargets();
}
void TestGameApp::RenderShadowDepthMap(const RenderContext* pRenderContext)
{
	m_pShadowDepthMapPtr->Begin();
		m_pLevel->DrawShadowMap(pRenderContext);
	m_pShadowDepthMapPtr->End();

	m_pShadowDepthTexturePtr = m_pShadowDepthMapPtr->GetDepthMap();
	ResetRenderTargets();
}
D3DXPLANE TestGameApp::CreatePlane(D3DXVECTOR3 &point, D3DXVECTOR3 &normal)
{
	D3DXPLANE plane;
	D3DXPlaneFromPointNormal(&plane, &point, &normal);
	D3DXPlaneNormalize(&plane, &plane);

	return plane;
}